package com.quiz.bis.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.quiz.bis.domain.bo.QuestionBo;
import com.quiz.bis.domain.bo.QuestionExportBo;
import com.quiz.bis.domain.model.*;
import com.quiz.bis.domain.vo.*;
import com.quiz.bis.mapper.*;
import com.quiz.bis.service.IQuestionService;
import com.quiz.bis.service.ITextSimilarityService;
import com.quiz.bis.template.pdf.QuestionHTML;
import com.quiz.bis.util.pdf.PdfUtil;
import com.quiz.bis.util.text.SimHash;
import com.quiz.bis.util.text.TextPreprocessor;
import com.quiz.common.core.domain.R;
import com.quiz.common.exception.ServiceException;
import com.quiz.common.mybatis.core.page.PageQuery;
import com.quiz.common.mybatis.core.page.TableDataInfo;
import com.quiz.common.mybatis.core.query.MPJLambdaWrapperPlus;
import com.quiz.common.mybatis.core.query.PlusWrappers;
import com.quiz.common.utils.DateUtils;
import com.quiz.common.utils.SecurityUtils;
import com.quiz.common.utils.StringUtils;
import com.quiz.common.utils.file.FileUtils;
import com.quiz.file.domain.vo.FileInfoVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 题目服务层实现
 *
 * @author ahuan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class QuestionServiceImpl implements IQuestionService {
    private final QuestionMapper questionMapper;
    private final QuestionTypeMapper questionTypeMapper;
    private final QuestionOptionMapper questionOptionMapper;
    private final QuestionAnswerMapper questionAnswerMapper;
    private final QuestionCategoryMapper questionCategoryMapper;
    private final QuestionCategoryRelationMapper questionCategoryRelationMapper;
    private final QuestionTagRelationMapper questionTagRelationMapper;
    private final QuestionFingerprintMapper fingerprintMapper;
    private final TextSimilarityServiceImpl similarityService;
    private final TextPreprocessor textPreprocessor;
    private final ITextSimilarityService textSimilarityService;
    private final QuestionHTML questionHTML;

    @Override
    public TableDataInfo<QuestionVo> selectPageQuestionList(QuestionQuery query, PageQuery page) {
        Page<QuestionVo> pageResult = questionMapper.selectQuestionList(page.build(), query);
        return TableDataInfo.build(pageResult);
    }

    @Override
    public TableDataInfo<QuestionDetailVo> fulltextSearch(String keyword, Long categoryId, PageQuery page) {
        Page<QuestionDetailVo> pageResult = questionMapper.fulltextSearch(page.build(), keyword, categoryId);

        // 处理高亮
        pageResult.getRecords().forEach(question -> {
            question.setTitle(highlightKeywords(question.getTitle(), keyword));
            question.setContent(highlightHtmlContent(question.getContent(), keyword));
        });

        return TableDataInfo.build(pageResult);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteQuestionByIds(Long[] ids) {
        if (ids == null || ids.length == 0) {
            throw new IllegalArgumentException("删除ID不能为空");
        }
        List<Long> idList = Arrays.asList(ids);

        // 1. 检查题目是否存在

        Long actualCount = questionMapper.selectCountByIds(ids);
        if (actualCount != ids.length) {
            throw new ServiceException("部分题目不存在或已被删除");
        }

        // 2. 删除题目及相关关联数据
        // 先删除关联表数据（如题目-分类关联、题目-标签关联等）
        LambdaUpdateWrapper<QuestionCategoryRelation> wrapperCat = PlusWrappers.lambdaUpdate(QuestionCategoryRelation.class).in(QuestionCategoryRelation::getQuestionId, idList);
        LambdaUpdateWrapper<QuestionTagRelation> wrapperTag = PlusWrappers.lambdaUpdate(QuestionTagRelation.class).in(QuestionTagRelation::getQuestionId, idList);
        questionCategoryRelationMapper.delete(wrapperCat);
        questionTagRelationMapper.delete(wrapperTag);


        // 最后删除题目主表
        return questionMapper.deleteBatchIds(idList) > 0;
    }


    @Override
    public QuestionDetailVo selectQuestionById(Long id) {
        Question question = questionMapper.selectById(id);
        if (question == null) {
            throw new ServiceException("题目不存在");
        }

        QuestionDetailVo vo = BeanUtil.copyProperties(question, QuestionDetailVo.class);


        // 设置题型名称
        QuestionType type = questionTypeMapper.selectById(question.getTypeId());
        if (type != null) {
            vo.setTypeName(type.getName());
        }

        // 查询选项
        vo.setOptions(questionOptionMapper.selectList(
                Wrappers.<QuestionOption>lambdaQuery()
                        .eq(QuestionOption::getQuestionId, id)
        ));

        // 查询答案
        vo.setAnswers(questionAnswerMapper.selectList(
                Wrappers.<QuestionAnswer>lambdaQuery()
                        .eq(QuestionAnswer::getQuestionId, id)
        ));

        // 查询分类
        List<QuestionCategory> categories = questionCategoryMapper.selectCategoriesByQuestionId(id);
        vo.setCategories(categories.stream()
                .map(QuestionCategory::getName)
                .collect(Collectors.joining(",")));

        vo.setCategoryIds(categories.stream()
                .map(c -> c.getId().toString())
                .collect(Collectors.joining(",")));

        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateQuestion(QuestionBo bo) {
        // 1. 验证题目是否存在
        Question oldQuestion = questionMapper.selectById(bo.getId());
        if (oldQuestion == null) {
            throw new ServiceException("题目不存在");
        }

        // 2. 更新题目基本信息
        Question question = BeanUtil.copyProperties(bo, Question.class);

        // 处理附件ID数组转字符串
        if (bo.getAttachmentList() != null) {
            question.setAttachmentIds(CollUtil.join(bo.getAttachmentList(), ","));
        }

        // 设置更新人信息
        Long userId = SecurityUtils.getUserId();
        question.setUpdateBy(String.valueOf(userId));

        // 生成SimHash
        ProcessedText processedText = generateSimHash(bo);
        question.setSimHash(similarityService.computeSimHash(processedText));
        question.setProcessedText(JSONUtil.toJsonStr(processedText));

        int rows = questionMapper.updateById(question);
        if (rows <= 0) {
            throw new ServiceException("更新题目失败");
        }

        // 3. 更新选项（先删后增）
        if (oldQuestion.getTypeId().equals(bo.getTypeId())) {
            // 题型未变，正常更新选项
            updateOptions(bo);
        } else {
            // 题型变化，清空原有选项
            questionOptionMapper.delete(
                    Wrappers.<QuestionOption>lambdaQuery()
                            .eq(QuestionOption::getQuestionId, bo.getId())
            );

            // 如果新题型有选项，则添加
            QuestionType newType = questionTypeMapper.selectById(bo.getTypeId());
            if (newType != null && newType.getHasOption() > 0 && bo.getOptions() != null) {
                addOptions(bo);
            }
        }

        // 4. 更新答案（先删后增）
        questionAnswerMapper.delete(
                Wrappers.<QuestionAnswer>lambdaQuery()
                        .eq(QuestionAnswer::getQuestionId, bo.getId())
        );

        QuestionType type = questionTypeMapper.selectById(bo.getTypeId());
        if (type != null && type.getHasAnswer() != 0 && bo.getAnswers() != null) {
            addAnswers(bo);
        }

        // 5. 更新分类关联（先删后增）
        questionCategoryRelationMapper.delete(
                Wrappers.<QuestionCategoryRelation>lambdaQuery()
                        .eq(QuestionCategoryRelation::getQuestionId, bo.getId())
        );

        if (bo.getCategoryIds() != null) {
            addCategoryRelations(bo);
        }

        // 6. 更新标签关联（先删后增）
        questionTagRelationMapper.delete(
                Wrappers.<QuestionTagRelation>lambdaQuery()
                        .eq(QuestionTagRelation::getQuestionId, bo.getId())
        );

        if (bo.getTagIds() != null) {
            addTagRelations(bo);
        }

        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Long> addQuestion(QuestionBo bo) {
        // 构建题目对象
        Question question = BeanUtil.copyProperties(bo, Question.class);
        String attachmentIds = bo.getAttachmentList().stream().map(FileInfoVo::getFileId).collect(Collectors.joining(","));
        question.setAttachmentIds(attachmentIds);

        // 验证题型是否存在
        QuestionType questionType = questionTypeMapper.selectById(question.getTypeId());
        if (questionType == null) {
            throw new ServiceException("题型不存在");
        }

        // 设置创建人信息
        Long userId = SecurityUtils.getUserId();
        question.setCreateBy(String.valueOf(userId));
        question.setUpdateBy(String.valueOf(userId));

        // 生成simHash指纹
        ProcessedText processedText = generateSimHash(bo);
        question.setSimHash(similarityService.computeSimHash(processedText));
        question.setProcessedText(JSONUtil.toJsonStr(processedText));

        // 保存题目基本信息
        questionMapper.insert(question);
        Long questionId = question.getId();

        // 保存选项
        if (questionType.getHasOption() > 0 && question.getOptions() != null) {
            ArrayList<QuestionOption> optionsInsert = new ArrayList<>();
            for (QuestionOption option : question.getOptions()) {
                option.setQuestionId(questionId);
                optionsInsert.add(option);
            }
            questionOptionMapper.insertBatch(optionsInsert);
        }

        // 保存答案
        if (questionType.getHasAnswer() != 0 && question.getAnswers() != null) {
            ArrayList<QuestionAnswer> answersInsert = new ArrayList<>();
            for (QuestionAnswer answer : question.getAnswers()) {
                answer.setQuestionId(questionId);
                ProcessedText answerText = textPreprocessor.preprocess(answer.getContent());
                answer.setProcessedText(JSONUtil.toJsonStr(answerText));
                answersInsert.add(answer);
            }
            questionAnswerMapper.insertBatch(answersInsert);
        }

        // 保存分类关联
        if (question.getCategoryIds() != null) {
            List<QuestionCategoryRelation> relations = new ArrayList<>();
            for (Long categoryId : question.getCategoryIds()) {
                QuestionCategoryRelation relation = new QuestionCategoryRelation();
                relation.setQuestionId(questionId);
                relation.setCategoryId(categoryId);
                // 0-分类
                relation.setIsKnowledge(0);
                relations.add(relation);
            }
            questionCategoryRelationMapper.insertBatch(relations);
        }

        // 保存标签关联
        if (question.getTagIds() != null) {
            List<QuestionTagRelation> tags = new ArrayList<>();
            for (Long tagId : question.getTagIds()) {
                QuestionTagRelation relation = new QuestionTagRelation();
                relation.setQuestionId(questionId);
                relation.setTagId(tagId);
                tags.add(relation);
            }
            questionTagRelationMapper.insertBatch(tags);
        }

        return R.ok(questionId);
    }


    private void updateOptions(QuestionBo bo) {
        // 删除不存在的选项
        List<Long> newOptionIds = bo.getOptions().stream()
                .filter(opt -> opt.getId() != null)
                .map(QuestionOption::getId)
                .collect(Collectors.toList());

        if (!newOptionIds.isEmpty()) {
            questionOptionMapper.delete(
                    Wrappers.<QuestionOption>lambdaQuery()
                            .eq(QuestionOption::getQuestionId, bo.getId())
                            .notIn(QuestionOption::getId, newOptionIds)
            );
        } else {
            questionOptionMapper.delete(
                    Wrappers.<QuestionOption>lambdaQuery()
                            .eq(QuestionOption::getQuestionId, bo.getId())
            );
        }

        // 更新或新增选项
        for (QuestionOption option : bo.getOptions()) {
            option.setQuestionId(bo.getId());
            if (option.getId() != null) {
                questionOptionMapper.updateById(option);
            } else {
                questionOptionMapper.insert(option);
            }
        }
    }

    private void addOptions(QuestionBo bo) {
        for (QuestionOption option : bo.getOptions()) {
            option.setQuestionId(bo.getId());
            questionOptionMapper.insert(option);
        }
    }

    private void addAnswers(QuestionBo bo) {
        for (QuestionAnswer answer : bo.getAnswers()) {
            answer.setQuestionId(bo.getId());
            if(answer.getId() == null){
                questionAnswerMapper.insert(answer);
            }else {
                questionAnswerMapper.update(
                        answer,
                        Wrappers.<QuestionAnswer>lambdaQuery()
                                .eq(QuestionAnswer::getId, answer.getId())
                );
            }
        }
    }

    private void addCategoryRelations(QuestionBo bo) {
        for (Long categoryId : bo.getCategoryIds()) {
            QuestionCategoryRelation relation = new QuestionCategoryRelation();
            relation.setQuestionId(bo.getId());
            relation.setCategoryId(categoryId);
            relation.setIsKnowledge(0); // 0-分类
            if(categoryId == null) {
                questionCategoryRelationMapper.insert(relation);
            }else {
                questionCategoryRelationMapper.update(
                        relation,
                        Wrappers.<QuestionCategoryRelation>lambdaQuery()
                                .eq(QuestionCategoryRelation::getQuestionId, bo.getId())
                                .eq(QuestionCategoryRelation::getCategoryId, categoryId)
                );
            }
        }
    }

    private void addTagRelations(QuestionBo bo) {
        for (Long tagId : bo.getTagIds()) {
            QuestionTagRelation relation = new QuestionTagRelation();
            relation.setQuestionId(bo.getId());
            relation.setTagId(tagId);
            questionTagRelationMapper.insert(relation);
        }
    }


    private String highlightKeywords(String text, String keyword) {
        if (StringUtils.isBlank(text) || StringUtils.isBlank(keyword)) {
            return text;
        }
        return text.replaceAll("(?i)(" + Pattern.quote(keyword) + ")", "<span style='color:red;font-weight:bold;'>$1</span>");
    }

    private String highlightHtmlContent(String html, String keyword) {
        if (StringUtils.isBlank(html) || StringUtils.isBlank(keyword)) {
            return html;
        }

        Document doc = Jsoup.parse(html);
        String regex = "(?i)(" + Pattern.quote(keyword) + ")";
        processTextNodes(doc.body(), regex, 0); // 限制递归深度
        return doc.body().html();
    }

    private void processTextNodes(Element element, String regex, int depth) {
        if (depth > 100) {
            return;
        } // 防止无限递归

        List<TextNode> textNodes = new ArrayList<>(element.textNodes()); // 缓存文本节点

        for (TextNode textNode : textNodes) {
            String text = textNode.text();
            if (text.matches(".*" + regex + ".*")) {
                String highlighted = text.replaceAll(regex,
                        "<span style='color:red;font-weight:bold;'>$1</span>");
                textNode.before(highlighted);
                textNode.remove();
            }
        }

        for (Element child : element.children()) {
            processTextNodes(child, regex, depth + 1);
        }
    }


    @Override
    public List<QuestionExportVo> exportQuestionList(QuestionExportBo query) {
        // 获取题目基础信息
        List<QuestionDetailVo> questions = query.getQuestionIds() != null && !query.getQuestionIds().isEmpty()
                ? questionMapper.selectQuestionListByIds(query.getQuestionIds())
                : questionMapper.selectQuestionListByQuery(query);

        if (questions.isEmpty()) {
            return Collections.emptyList();
        }

        // 获取所有题目ID
        List<Long> questionIds = questions.stream()
                .map(QuestionDetailVo::getId)
                .collect(Collectors.toList());

        // 批量查询选项和答案
        Map<Long, List<QuestionOption>> optionsMap = questionMapper.selectOptionsByQuestionIds(questionIds)
                .stream()
                .collect(Collectors.groupingBy(QuestionOption::getQuestionId));

        Map<Long, List<QuestionAnswer>> answersMap = questionMapper.selectAnswersByQuestionIds(questionIds)
                .stream()
                .collect(Collectors.groupingBy(QuestionAnswer::getQuestionId));

        // 设置选项和答案到题目中
        questions.forEach(question -> {
            question.setOptions(optionsMap.getOrDefault(question.getId(), Collections.emptyList()));
            question.setAnswers(answersMap.getOrDefault(question.getId(), Collections.emptyList()));
        });

        // 按题型分组并排序
        return questions.stream()
                .collect(Collectors.groupingBy(q -> new AbstractMap.SimpleEntry<>(
                        q.getTypeId(),
                        q.getTypeName()
                )))
                .entrySet().stream()
                .map(entry -> {
                    QuestionExportVo vo = new QuestionExportVo();
                    vo.setTypeId(entry.getKey().getKey());
                    vo.setTypeName(entry.getKey().getValue());
                    vo.setQuestions(entry.getValue());
                    return vo;
                })
                .sorted(Comparator.comparing(QuestionExportVo::getTypeId))
                .collect(Collectors.toList());
    }


    @Override
    public void exportToPdf(List<QuestionExportVo> questions, HttpServletResponse response,
                            HttpServletRequest request, QuestionExportBo query) {
        // 生成html模版
        String html = questionHTML.builder(questions, request, query);
        PdfUtil.exportToPdf(html, "题目列表导出.pdf", response);
    }



    @Override
    public void exportToWord(List<QuestionExportVo> questions, HttpServletResponse response,
                             HttpServletRequest request, QuestionExportBo query) {
        try {
            String filename = "题目列表_" + DateUtils.dateTimeNow() + ".docx";
            FileUtils.setAttachmentResponseHeader(response, filename);
//            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            response.setContentType("application/octet-stream");

            XWPFDocument document = new XWPFDocument();

            // 添加标题
            XWPFParagraph titlePara = document.createParagraph();
            titlePara.setAlignment(ParagraphAlignment.CENTER);
            XWPFRun titleRun = titlePara.createRun();
            titleRun.setText("题目列表");
            titleRun.setFontSize(16);
            titleRun.setBold(true);
            titleRun.addBreak();

            // 添加时间
            XWPFParagraph timePara = document.createParagraph();
            timePara.setAlignment(ParagraphAlignment.RIGHT);
            XWPFRun timeRun = timePara.createRun();
            timeRun.setText("导出时间: " + DateUtils.getTime());
            timeRun.setFontSize(12);
            timeRun.addBreak();

            // 添加题型和题目
            for (QuestionExportVo typeGroup : questions) {
                // 题型标题
                XWPFParagraph typePara = document.createParagraph();
                typePara.setSpacingBefore(400);
                XWPFRun typeRun = typePara.createRun();
                typeRun.setText(typeGroup.getTypeName());
                typeRun.setFontSize(14);
                typeRun.setBold(true);
                typeRun.addBreak();

                // 题目列表
                int index = 1;
                for (QuestionDetailVo question : typeGroup.getQuestions()) {
                    // 题目序号和内容
                    XWPFParagraph questionPara = document.createParagraph();
                    XWPFRun questionRun = questionPara.createRun();
                    questionRun.setText(index + ". " + question.getTitle());
                    questionRun.setFontSize(12);
                    questionRun.addBreak();

                    // 选项
                    if (question.getOptions() != null && !question.getOptions().isEmpty()) {
                        XWPFTable optionTable = document.createTable(question.getOptions().size(), 2);
                        optionTable.setWidth("90%");

                        int rowIndex = 0;
                        for (QuestionOption option : question.getOptions()) {
                            // 选项内容
                            XWPFTableCell optionCell = optionTable.getRow(rowIndex).getCell(0);
                            XWPFParagraph optionPara = optionCell.getParagraphs().get(0);
                            optionPara.setAlignment(ParagraphAlignment.LEFT);
                            XWPFRun optionRun = optionPara.createRun();
                            optionRun.setText(option.getOptionCode() + ". " + option.getContent());
                            optionCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);

                            // 正确答案标记
                            XWPFTableCell correctCell = optionTable.getRow(rowIndex).getCell(1);
                            XWPFParagraph correctPara = correctCell.getParagraphs().get(0);
                            correctPara.setAlignment(ParagraphAlignment.CENTER);
                            XWPFRun correctRun = correctPara.createRun();
                            if (option.getIsCorrect() == 1) {
                                correctRun.setText("✓");
                            } else {
                                correctRun.setText("");
                            }
                            correctCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
                            rowIndex++;
                        }
                    }

                    // 答案
                    if (query.getIncludeAnswer() && question.getAnswers() != null && !question.getAnswers().isEmpty()) {
                        XWPFParagraph answerPara = document.createParagraph();
                        XWPFRun answerRun = answerPara.createRun();
                        answerRun.setText("答案: ");
                        for (QuestionAnswer answer : question.getAnswers()) {
                            answerRun.setText(answerRun.getText(0) + answer.getContent());
                        }
                        answerRun.setItalic(true);
                        answerRun.setColor("808080");
                        answerRun.addBreak();
                    }

                    // 解析
                    if (query.getIncludeAnalysis() && StringUtils.isNotEmpty(question.getAnalysis())) {
                        XWPFParagraph analysisPara = document.createParagraph();
                        XWPFRun analysisRun = analysisPara.createRun();
                        analysisRun.setText("解析: " + question.getAnalysis());
                        analysisRun.setItalic(true);
                        analysisRun.setColor("808080");
                        analysisRun.addBreak();
                    }

                    document.createParagraph().createRun().addBreak();
                    index++;
                }
            }

            document.write(response.getOutputStream());
            document.close();
        } catch (Exception e) {
            log.error("导出Word失败", e);
            throw new ServiceException("导出Word失败，请稍后再试");
        }
    }

    @Override
    public TableDataInfo<QuestionDetailVo> querySimilarQuestions(Long questionId, PageQuery page, Double threshold) {
        Question question = questionMapper.selectById(questionId);

        // 检验并设置相似度
//        final Double thresholdFinal;
//        if (threshold == null) {
//            thresholdFinal = 0.4;
//        } else if (threshold < 0 || threshold > 1) {
//            throw new ServiceException("相似度阈值必须在0到1之间");
//        } else {
//            thresholdFinal = threshold;
//        }

        // 预处理题目文本
        ProcessedText processed = textPreprocessor.preprocess(question.getContent());
        long simHash = SimHash.computeSimHash(processed.getWords());

        // 先用SimHash筛选可能相似的分页题目列表
        Page<QuestionDetailVo> candidates = questionMapper.findSimilarBySimHash(page.build(), simHash, questionId);

        // 计算混合相似度
        candidates.getRecords().forEach(candidate -> {
            double similarity = textSimilarityService.calculateSimilarity(processed, JSONUtil.toBean(candidate.getProcessedText(), ProcessedText.class));
            candidate.setSimilarity(similarity);
        });
        // 根据相似度降序排序
        candidates.getRecords().sort((o1, o2) -> Double.compare(o2.getSimilarity(), o1.getSimilarity()));

        return TableDataInfo.build(candidates);
    }

    @Override
    public TableDataInfo<QuestionDetailVo> querySimilarQuestions(String questionHtml, PageQuery page, Double threshold) {

        // 检验并设置相似度
//        final Double thresholdFinal;
//        if (threshold == null) {
//            thresholdFinal = 0.4;
//        } else if (threshold < 0 || threshold > 1) {
//            throw new ServiceException("相似度阈值必须在0到1之间");
//        } else {
//            thresholdFinal = threshold;
//        }

        // 预处理题目文本
        ProcessedText processed = textPreprocessor.preprocess(questionHtml);
        long simHash = SimHash.computeSimHash(processed.getWords());

        // 先用SimHash筛选可能相似的分页题目列表
        Page<QuestionDetailVo> candidates = questionMapper.findSimilarBySimHash(page.build(), simHash, null);

        // 计算混合相似度
        candidates.getRecords().forEach(candidate -> {
            double similarity = textSimilarityService.calculateSimilarity(processed, JSONUtil.toBean(candidate.getProcessedText(), ProcessedText.class));
            candidate.setSimilarity(similarity);
        });
        // 根据相似度降序排序
        candidates.getRecords().sort((o1, o2) -> Double.compare(o2.getSimilarity(), o1.getSimilarity()));

        return TableDataInfo.build(candidates);
    }


    private ProcessedText generateSimHash(QuestionBo bo) {
        StringBuilder builder = new StringBuilder();
        builder.append(bo.getContent());
        if (CollUtil.isNotEmpty(bo.getOptions())) {
            bo.getOptions().stream().forEach(option -> {
                builder.append(option.getContent());
            });
        }
        if (CollUtil.isNotEmpty(bo.getAnswers())) {
            bo.getAnswers().stream().forEach(answer -> {
                builder.append(answer.getContent());
            });
        }
        return textPreprocessor.preprocess(builder.toString());
    }

    @Override
    public List<QuestionDetailVo> findSimilarQuestions(Long questionId, int count) {
        QuestionDetailVo question = questionMapper.selectQuestionById(questionId);
        if (question == null) {
            throw new ServiceException("题目不存在");
        }

        // 使用SimHash和文本相似度查找相似题目
        Page<QuestionDetailVo> similarPage = questionMapper.findSimilarBySimHash(
                new Page<>(1, count),
                question.getSimHash(),
                questionId
        );

        // 计算混合相似度并排序
        similarPage.getRecords().forEach(item -> {
            double similarity = textSimilarityService.calculateSimilarity(
                    JSONUtil.toBean(question.getProcessedText(), ProcessedText.class),
                    JSONUtil.toBean(item.getProcessedText(), ProcessedText.class)
            );
            item.setSimilarity(similarity);
        });

        // 按相似度降序排序
        similarPage.getRecords().sort((o1, o2) -> Double.compare(o2.getSimilarity(), o1.getSimilarity()));

        // 转换为Question对象列表
        return similarPage.getRecords();
    }

    @Override
    public List<QuestionDetailVo> findByKnowledgeDifferentDifficulty(Long questionId, Long typeId, Integer difficulty, int count) {
        // 1. 获取题目关联的知识点
        List<Long> knowledgeIds = questionCategoryRelationMapper.selectList(
                Wrappers.<QuestionCategoryRelation>lambdaQuery()
                        .eq(QuestionCategoryRelation::getQuestionId, questionId)
//                        .eq(QuestionCategoryRelation::getIsKnowledge, 1)
        ).stream().map(QuestionCategoryRelation::getCategoryId).collect(Collectors.toList());

        if (knowledgeIds.isEmpty()) {
            return Collections.emptyList();
        }

        // 2. 查找同知识点但不同难度的题目
        List<Long> questionIds = questionCategoryRelationMapper.selectList(
                Wrappers.<QuestionCategoryRelation>lambdaQuery()
                        .in(QuestionCategoryRelation::getCategoryId, knowledgeIds)
        ).stream().map(QuestionCategoryRelation::getQuestionId).collect(Collectors.toList());
        Page<QuestionDetailVo> byKnowledgeDifferentDifficulty = questionMapper.findByKnowledgeDifferentDifficulty(new Page<>(1, count), typeId, questionIds);
        return byKnowledgeDifferentDifficulty.getRecords();
    }

    @Override
    public List<QuestionDetailVo> findByDifficultyDifferentKnowledge(Long questionId, Long typeId, Integer difficulty, int count) {
        // 1. 获取题目关联的知识点
        List<Long> knowledgeIds = questionCategoryRelationMapper.selectList(
                Wrappers.<QuestionCategoryRelation>lambdaQuery()
                        .eq(QuestionCategoryRelation::getQuestionId, questionId)
//                        .eq(QuestionCategoryRelation::getIsKnowledge, 1)
        ).stream().map(QuestionCategoryRelation::getCategoryId).collect(Collectors.toList());

        if (knowledgeIds.isEmpty()) {
            return Collections.emptyList();
        }

        // 2. 查找同难度但不同知识点的题目
        List<Long> questionIds = questionCategoryRelationMapper.selectList(
                Wrappers.<QuestionCategoryRelation>lambdaQuery()
                        .in(QuestionCategoryRelation::getCategoryId, knowledgeIds)
//                                                .eq(QuestionCategoryRelation::getIsKnowledge, 1)
        ).stream().map(QuestionCategoryRelation::getQuestionId).collect(Collectors.toList());
        Page<QuestionDetailVo> byDifficultyDifferentKnowledge = questionMapper.findByDifficultyDifferentKnowledge(new Page<>(1, count), typeId, difficulty, questionIds);
        return byDifficultyDifferentKnowledge.getRecords();
    }

    @Override
    public List<QuestionDetailVo> findRandomQuestions(Long typeId, int count) {
        // 使用数据库随机函数获取随机题目
        MPJLambdaWrapper<Question> wrapper = new MPJLambdaWrapperPlus<Question>()
                .eq(typeId != null, "type_id", typeId)
                .orderByAsc("rand()")
                .last("LIMIT " + count);
        return questionMapper.findRandomQuestions(new Page<>(1, count), typeId);

    }

    @Override
    public TableDataInfo<QuestionDetailVo> selectPageQuestionList2(QuestionQuery query, PageQuery page) {
        Page<QuestionDetailVo> pageResult = questionMapper.selectQuestionList2(page.build(), query);
        return TableDataInfo.build(pageResult);
    }


}